using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DataStructures;

namespace ShellAnywhere.Model
{
	using IParamToken = IToken<IParam>;
	using ParamToken = Token<IParam>;
	using CharStream = Stream<char>;
	using ParamTokenStream = Stream<IToken<IParam>>;	

	public class Lexer : StreamProcessor<char, IParamToken>
	{
		#region ctors
		public Lexer(CharStream inputStream, ParamTokenStream outputStream, bool allowEmptyMatch= false) : base(inputStream, outputStream)
		{
			AllowEmptyMatch = allowEmptyMatch;
			_ValidChars.Clear();
			_ValidChars.Add('\0');
			for (char c = ' '; c < '~'; ++c)
				_ValidChars.Add(c);
		}
		#endregion ctors

		#region properties
		public bool IsCaseSensitive { get; set; }
		public bool AllowEmptyMatch { get; set; }
		public override CharStream InputStream { get; set; }
		public override ParamTokenStream OutputStream { get; set; }

		public bool IsEndOfToken
		{
			get
			{
				if (InputStream.Position > 0 && !InputStream.IsEmpty)
					return InputStream.PrevItem() == default(char);
				return false;
			}
		}
		public bool IsWhiteSpace
		{
			get { return InputStream.PeekItem() == ' '; }
		}
		public bool IsEndOfStream
		{
			get { return InputStream.IsEndOfStream; }
		}
		public bool IsLastCharOfToken
		{
			get
			{
				return _StateTree.Value.IsMatched;
			}
		}
		public string BacktrackToken()
		{
			string backtrackToken = "";

			char c;
			int index = InputStream.Count - 1;
			while (index >= 0 && (c = InputStream.Items[index]) != ' ')
			{
				backtrackToken = backtrackToken.Insert(0, c.ToString());
				--index;
			}

			InputStream.Position = InputStream.Items.Count;
			return backtrackToken;
		}
		#endregion properties

		#region methods
		public override IParamToken NextOutputItem()
		{
			IParamToken token = (AllowEmptyMatch && InputStream.IsEmpty && OutputStream.IsEmpty) ? _StateTree.Root.Value : null;
			ITree<IParamToken> current = _StateTree;
			while (!IsEndOfToken && !IsEndOfStream)
			{
				char c = InputStream.NextItem();

				current = current.Children.Find(t => t.Value.LastChar == c);

				if (current != null && (token = current.Value) != null)
				{
					if (IsLastCharOfToken)
						break;
				}
				else
				{
					string backtrack = BacktrackToken();
					IParam param = new Param(backtrack);
					token = new ParamToken(backtrack.Length, param);
					break;
				}
			}
			return token;
		}
		//public void InitStateTree(TargetList targets)
		public void InitStateTree(List<ITarget> targets)
		{
			IParamToken initial = new ParamToken(targets); //changed ctor for ParamToken
			_StateTree = new Tree<IToken<IParam>>(initial);

			try
			{
				NextStates(_StateTree); //recursive until 'done'				
			}
			catch (Exception ex)
			{
				Console.WriteLine(ex.Message);
			}

			string tostring = "";
			_StateTree.TraversalType = TraversalType.TopDownLeftToRight;
			foreach (IParamToken token in _StateTree.Elements)
			{
				tostring += " {" + token.Dot.ToString() + "," + token.CharAtDot.ToString() + "} ";
			}			
		}

		#endregion methods

		#region fields
		private ITree<IParamToken> _StateTree;
		private static List<char> _ValidChars = new List<char>();
		#endregion fields

		#region init parsing methods
		private void NextStates(ITree<IParamToken> tree)
		{
			try
			{
				foreach (char c in _ValidChars)
				{
					IParamToken temp = NextState(tree.Value, c);
					IParamToken next = E_Closure(temp);
					if (next != null && next.Count > 0)
						tree.Children.Add(next);
				}
				foreach (ITree<IToken<IParam>> child in tree.Children)
					NextStates(child);
			}
			catch (Exception ex)
			{
				Console.WriteLine(ex.Message);
			}
		}
		private IParamToken NextState(IParamToken current, char c)
		{
			IParamToken next = null;			
			if (current != null)
			{
				List<IParam> children = new List<IParam>();
				foreach (IParam param in current.Children)
				{
					if (c == param.CharAt(current.Dot))
						children.Add(param);
				}
				if (children.Count > 0)
					next = new ParamToken(current.Dot, children); //should it not be one more? maybe...
			}
			return next;
		}
		private IParamToken E_Closure(IParamToken state)
		{
			IParamToken closure = null;			
			if (state != null)
			{
				List<IParam> children = new List<IParam>();
				foreach (IParam param in state.Children)
				{
					if (!param.IsMatched(state.Dot))
						children.Add(param);
				}
				closure = new ParamToken(state.Dot + 1, children);
			}
			return closure;
		}
		#endregion init parsing methods
	}
}
